package com.ai.aiagent.app;

import com.ai.aiagent.advisor.MyLoggerAdvisor;
import com.ai.aiagent.advisor.PermissionValidationAdvisor;
import com.ai.aiagent.advisor.WordsCheckAdvisor;
import com.ai.aiagent.chatmemory.FileBasedChatMemory;
import com.ai.aiagent.chatmemory.MysqlChatMemory;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.DefaultChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;

import java.util.ArrayList;

@Component
@Slf4j
public class LoveApp {
    private final ChatClient chatClient;

    private static final String SYSTEM_PROMPT = "扮演深耕恋爱心理领域的专家。开场向用户表明身份，告知用户可倾诉恋爱难题。" +
            "围绕单身、恋爱、已婚三种状态提问：单身状态询问社交圈拓展及追求心仪对象的困扰；" +
            "恋爱状态询问沟通、习惯差异引发的矛盾；已婚状态询问家庭责任与亲属关系处理的问题。" +
            "引导用户详述事情经过、对方反应及自身想法，以便给出专属解决方案。";

//    @Resource
//    private MysqlChatMemory chatMemory;

    public LoveApp(ChatModel dashscopeChatModel) {
        // 初始化一个基于内存的对话记忆
//        MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder().build();

        // 使用基于文件持久化的会话记忆-自定义实现
//        String dir = System.getProperty("user.dir") + "/tmp/chat-memory";
//        FileBasedChatMemory chatMemory = new FileBasedChatMemory(dir);

        // 使用基于mysql持久化的会话记忆-自定义实现
        MysqlChatMemory chatMemory = new MysqlChatMemory();
        this.chatClient = ChatClient.builder(dashscopeChatModel)
                .defaultSystem(SYSTEM_PROMPT) // 设置系统预设，系统的角色
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build(), // 添加对话记忆拦截器
                        MyLoggerAdvisor.builder().order(10).build(),// 添加自定义日志拦截器
                        PermissionValidationAdvisor.builder().build(),
                        WordsCheckAdvisor.builder().order(20).build())
//                .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build(), // 添加对话记忆拦截器
//                        MyLoggerAdvisor.builder().order(10).build(), // 添加自定义日志拦截器
//                        ReReadingAdvisor.builder().order(1).build()) // 添加增强拦截器，这个的优先级要高于日志拦截器，否则的话，不能打印出来增强后的用户prompt
                .build();
    }

    /**
     * 制作聊天客户端，同步call
     *
     * @param message
     * @param chatId
     * @return
     */
    public String doChat(String message, String chatId, String token) {

        ChatResponse chatResponse = chatClient.prompt()
                // 添加自定义参数,通过advisor可以将用户信息传入，之后在拦截器中获取用户信息进行权限校验
                .advisors(a -> a.param("user", token))
                .user(message) // 设置用户输入
                // chatclient通过advisor中设置的会话id和记录的条数来取聊天历史
                // 设置不同的会话id的话，也不会共享之前的会话记录
                .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId))
                .call()
                .chatResponse();
        String output = chatResponse.getResult().getOutput().getText();
        log.info("输出：{}", output);
        return output;
    }

    /**
     * 制作聊天客户端，异步stream
     *
     * @param message
     * @param chatId
     * @return
     */
    public Flux<ChatResponse> doChatStream(String message, String chatId, String token) {
        return chatClient.prompt()
                // 添加自定义参数,通过advisor可以将用户信息传入，之后在拦截器中获取用户信息进行权限校验
                .advisors(a -> a.param("user", token))
                .user(message) // 设置用户输入
                // chatclient通过advisor中设置的会话id和记录的条数来取聊天历史
                // 设置不同的会话id的话，也不会共享之前的会话记录
                .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId))
                .stream()
                .chatResponse()
                .doOnNext(response -> log.info("流式输出：{}", response.getResult().getOutput().getText()));
    }


    record ChatReport(String title, ArrayList<String> contents) {}
//    record ChatReport(String title, ArrayList<CharReportDetails> contents){}
//    record CharReportDetails(String type, String earth, List<String> reason, List<String> content){}

    /**
     * 制作生成报告的聊天客户端
     *
     * @param message
     * @param chatId
     * @return
     */
    public ChatReport doChatGetReport(String message, String chatId) {
        ChatReport chatReport = chatClient.prompt()
                .user(message) // 设置用户输入
                .system(SYSTEM_PROMPT + ",请根据结果生成恋爱报告，标题为{用户名}恋爱报告，内容为建议列表")

                // re2 过度重读问题（如>2次）可能导致模型过度关注输入而非生成结构化答案
//                .system(SYSTEM_PROMPT + ",请根据结果生成恋爱报告，标题为{用户名}恋爱报告，内容为建议列表，列表中：类型为旅游的主题；地点为旅游的目的地；原因为选择这个主题和地点的原因；内容为推荐的打卡事项。根据上述结构化内容构建回答内容")
//                .system(SYSTEM_PROMPT + ",请根据结果生成恋爱报告，标题为{用户名}恋爱报告，内容为建议列表" +
//                        "必须以严格的JSON格式输出，结构为：" +
//                        "[{" +
//                        "  \"title\": \"标题\"," +
//                        "  \"contents\": [" +
//                        "  \"type\": \"类型\"," +
//                        "  \"earth\": \"地点\"," +
//                        "  \"reason\": [原因1，原因2]" +
//                        "  \"content\": [内容1，内容2]" +
//                        "]" +
//                        "}]")
                // chatclient通过advisor中设置的会话id和记录的条数来取聊天历史
                // 设置不同的会话id的话，也不会共享之前的会话记录
                .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId))
                .call()
                .entity(ChatReport.class);
//                .entity(new ParameterizedTypeReference<List<ChatReport>>() {});
        log.info("输出报告：{}", chatReport);
        return chatReport;
    }
}
